package com.gzc.just.play.last.war.sceneserver.service;

import com.gzc.just.play.last.war.sceneserver.entity.Player;
import com.gzc.just.play.last.war.sceneserver.entity.PlayerAttribute;
import com.gzc.just.play.last.war.sceneserver.repository.PlayerRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * 玩家服务类
 * 
 * 提供玩家相关的业务逻辑
 * 作为Phase 4.3数据库持久化集成的一部分
 */
@Service
@Transactional
public class PlayerService {
    
    private static final Logger logger = LoggerFactory.getLogger(PlayerService.class);
    
    @Autowired
    private PlayerRepository playerRepository;
    
    @Autowired
    private PlayerAttributeService playerAttributeService;
    
    /**
     * 创建玩家
     */
    public Player createPlayer(String username, String passwordHash, String salt, String nickname) {
        // 检查用户名是否已存在
        if (playerRepository.findByUsername(username).isPresent()) {
            throw new RuntimeException("Username already exists: " + username);
        }
        
        // 检查昵称是否已存在
        if (playerRepository.findByNickname(nickname).isPresent()) {
            throw new RuntimeException("Nickname already exists: " + nickname);
        }
        
        // 创建玩家
        Player player = new Player(username, passwordHash, salt, nickname);
        player = playerRepository.save(player);
        
        // 创建玩家基础属性
        initializePlayerAttributes(player);
        
        logger.info("Created new player: {}", player);
        return player;
    }
    
    /**
     * 初始化玩家属性
     */
    private void initializePlayerAttributes(Player player) {
        // 生命值
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_HEALTH, 100);
        // 最大生命值
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_MAX_HEALTH, 100);
        // 法力值
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_MANA, 50);
        // 最大法力值
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_MAX_MANA, 50);
        // 攻击力
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_ATTACK, 10);
        // 防御力
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_DEFENSE, 5);
        // 移动速度
        playerAttributeService.createAttribute(player, PlayerAttribute.ATTRIBUTE_TYPE_SPEED, 100);
        
        logger.info("Initialized player attributes: {}", player.getPlayerId());
    }
    
    /**
     * 根据ID获取玩家
     */
    @Cacheable(value = "players", key = "#playerId")
    @Transactional(readOnly = true)
    public Optional<Player> getPlayerById(Long playerId) {
        return playerRepository.findById(playerId);
    }
    
    /**
     * 根据用户名获取玩家
     */
    @Cacheable(value = "players", key = "#username")
    @Transactional(readOnly = true)
    public Optional<Player> getPlayerByUsername(String username) {
        return playerRepository.findByUsername(username);
    }
    
    /**
     * 根据用户名和密码验证玩家
     */
    @Transactional(readOnly = true)
    public Optional<Player> authenticatePlayer(String username, String passwordHash) {
        return playerRepository.findByUsernameAndPasswordHash(username, passwordHash);
    }
    
    /**
     * 更新玩家信息
     */
    @CachePut(value = "players", key = "#player.playerId")
    public Player updatePlayer(Player player) {
        return playerRepository.save(player);
    }
    
    /**
     * 玩家登录
     */
    public Player loginPlayer(Long playerId) {
        Player player = playerRepository.findById(playerId)
                .orElseThrow(() -> new RuntimeException("Player not found: " + playerId));
        
        // 检查是否被封禁
        if (player.isCurrentlyBanned()) {
            throw new RuntimeException("Player is banned: " + player.getBanReason());
        }
        
        // 更新登录信息
        player.setIsOnline(true);
        player.setLastLoginTime(LocalDateTime.now());
        player.incrementLoginCount();
        
        player = playerRepository.save(player);
        logger.info("Player login: {}", playerId);
        
        return player;
    }
    
    /**
     * 玩家登出
     */
    public void logoutPlayer(Long playerId) {
        int updated = playerRepository.updatePlayerOnlineStatus(playerId, false, LocalDateTime.now());
        if (updated > 0) {
            logger.info("Player logout: {}", playerId);
        }
    }
    
    /**
     * 批量登出所有在线玩家
     */
    public int logoutAllOnlinePlayers() {
        int updated = playerRepository.batchUpdateLastLogoutTime(LocalDateTime.now());
        logger.info("Batch logout online players count: {}", updated);
        return updated;
    }
    
    /**
     * 增加玩家经验
     */
    @CacheEvict(value = "players", key = "#playerId")
    public Player addExperience(Long playerId, long experience) {
        Player player = playerRepository.findById(playerId)
                .orElseThrow(() -> new RuntimeException("Player not found: " + playerId));
        
        player.addExperience(experience);
        
        // 简单的升级逻辑：每1000经验升1级
        int newLevel = Math.min(100, 1 + (int)(player.getExperience() / 1000));
        if (newLevel > player.getLevel()) {
            player.setLevel(newLevel);
            // 升级时更新属性
            levelUpPlayerAttributes(player, newLevel);
        }
        
        player = playerRepository.save(player);
        logger.info("Player added experience: {} +{} Level: {}", playerId, experience, player.getLevel());
        
        return player;
    }
    
    /**
     * 玩家升级时更新属性
     */
    private void levelUpPlayerAttributes(Player player, int newLevel) {
        // Add basic attributes per level
        playerAttributeService.adjustAttributeValue(
                player.getPlayerId(), PlayerAttribute.ATTRIBUTE_TYPE_MAX_HEALTH, newLevel * 10);
        playerAttributeService.adjustAttributeValue(
                player.getPlayerId(), PlayerAttribute.ATTRIBUTE_TYPE_MAX_MANA, newLevel * 5);
        playerAttributeService.adjustAttributeValue(
                player.getPlayerId(), PlayerAttribute.ATTRIBUTE_TYPE_ATTACK, newLevel * 2);
        playerAttributeService.adjustAttributeValue(
                player.getPlayerId(), PlayerAttribute.ATTRIBUTE_TYPE_DEFENSE, newLevel);
        
        // Restore health and mana to full value
        playerAttributeService.resetAttributeToMax(player.getPlayerId(), PlayerAttribute.ATTRIBUTE_TYPE_HEALTH);
        playerAttributeService.resetAttributeToMax(player.getPlayerId(), PlayerAttribute.ATTRIBUTE_TYPE_MANA);
        
        logger.info("Player level up attributes updated: {} -> Level {}", player.getPlayerId(), newLevel);
    }
    
    /**
     * 封禁玩家
     */
    @CacheEvict(value = "players", key = "#playerId")
    public Player banPlayer(Long playerId, String reason, LocalDateTime endTime) {
        Player player = playerRepository.findById(playerId)
                .orElseThrow(() -> new RuntimeException("Player not found: " + playerId));
        
        player.banPlayer(reason, endTime);
        player = playerRepository.save(player);
        
        // 强制玩家下线
        player.setIsOnline(false);
        playerRepository.save(player);
        
        logger.info("Player banned: {} Reason: {} Expiry time: {}", playerId, reason, endTime);
        return player;
    }
    
    /**
     * 解除玩家封禁
     */
    @CacheEvict(value = "players", key = "#playerId")
    public Player unbanPlayer(Long playerId) {
        int updated = playerRepository.unbanPlayer(playerId);
        if (updated > 0) {
            logger.info("Player unbanned: {}", playerId);
        }
        
        return playerRepository.findById(playerId).orElse(null);
    }
    
    /**
     * 获取在线玩家列表
     */
    @Transactional(readOnly = true)
    public List<Player> getOnlinePlayers() {
        return playerRepository.findByIsOnlineTrue();
    }
    
    /**
     * 获取被封禁玩家列表
     */
    @Transactional(readOnly = true)
    public List<Player> getBannedPlayers() {
        return playerRepository.findCurrentlyBannedPlayers(LocalDateTime.now());
    }
    
    /**
     * 获取等级排行榜
     */
    @Transactional(readOnly = true)
    public Page<Player> getLevelLeaderboard(Pageable pageable) {
        return playerRepository.findLevelLeaderboard(1, pageable); // 只显示1级以上的玩家
    }
    
    /**
     * 根据等级范围获取玩家列表
     */
    @Transactional(readOnly = true)
    public Page<Player> getPlayersByLevelRange(Integer minLevel, Integer maxLevel, Pageable pageable) {
        return playerRepository.findByLevelBetween(minLevel, maxLevel, pageable);
    }
    
    /**
     * 获取今日注册的玩家
     */
    @Transactional(readOnly = true)
    public List<Player> getTodayRegisteredPlayers() {
        LocalDate today = LocalDate.now();
        LocalDateTime startOfDay = today.atStartOfDay();
        LocalDateTime endOfDay = today.plusDays(1).atStartOfDay();
        return playerRepository.findByCreateTimeBetween(startOfDay, endOfDay);
    }
    
    /**
     * 统计在线玩家数量
     */
    @Transactional(readOnly = true)
    public long countOnlinePlayers() {
        return playerRepository.countByIsOnlineTrue();
    }
    
    /**
     * 统计被封禁玩家数量
     */
    @Transactional(readOnly = true)
    public long countBannedPlayers() {
        return playerRepository.countCurrentlyBannedPlayers(LocalDateTime.now());
    }
    
    /**
     * 删除玩家
     */
    @CacheEvict(value = "players", key = "#playerId")
    public void deletePlayer(Long playerId) {
        if (!playerRepository.existsById(playerId)) {
            throw new RuntimeException("Player not found: " + playerId);
        }
        
        playerRepository.deleteById(playerId);
        logger.info("Deleted player: {}", playerId);
    }
}